#include "Socket.h"

Socket::Socket()
{
	this->conectado = false;
}

Socket::Socket(const unsigned int segundos, const unsigned int usegundos)
{
	this->conectado = false;
	this->tv.tv_sec = segundos;
	this->tv.tv_usec = usegundos;
}

Socket::Socket(int fileDescriptor)
{
	this->conectado = true;
	this->fileDescriptor = fileDescriptor;
}




Socket::~Socket()
{
	//	if(conectado){
	//		this->close();
	//	}//TODO ver si hacerlo a mano o no
}

bool Socket::create()
{
	this->fileDescriptor = socket(AF_INET, SOCK_STREAM, 0);
	return this->esValido();
}

bool Socket::connect(const string &hostname, unsigned int port)
{
	struct hostent *direccion_host;
	int status;

	if (this->conectado)
	{
		std::cerr << "ya esta conectado" << std::endl;
		return false;
	}

	if (!this->esValido())
	{
		std::cerr << "file descriptor invalido" << std::endl;
		return false;
	}

	direccion_host = gethostbyname(hostname.c_str());

	if (direccion_host == NULL)
	{
		std::cerr << "gethostbyname devuelve error" << std::endl;
		return false;
	}

	address.sin_family = AF_INET;
	address.sin_port = htons(port);
	address.sin_addr = *((struct in_addr*) direccion_host->h_addr);

	fcntl(this->fileDescriptor, F_SETFL, O_NONBLOCK);
	status = ::connect(this->fileDescriptor, (sockaddr*) &address, sizeof(address));

	if ((status == -1) && (errno == EINPROGRESS)){
		fd_set readfds;
		fd_set writefds;

		FD_ZERO(&readfds);
		FD_SET(this->fileDescriptor, &readfds);

		FD_ZERO(&writefds);
		FD_SET(this->fileDescriptor, &writefds);

		if (select(this->fileDescriptor+1, &readfds, &writefds, NULL, &tv) == 0) {
			errno = ETIMEDOUT;
			fcntl(this->fileDescriptor, F_SETFL, 0);
			return false;
		}

		int optval = 0;
		socklen_t optlen;

		if (FD_ISSET(this->fileDescriptor, &readfds) || FD_ISSET(this->fileDescriptor, &writefds)) {
			int optval = 0;
			socklen_t optlen = sizeof(optval);
			if (getsockopt(this->fileDescriptor, SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0) {
				fcntl(this->fileDescriptor, F_SETFL, 0);
				return false;
			}
		} else {
			fcntl(this->fileDescriptor, F_SETFL, 0);
			return false;
		}

		if(optval == 0){
			conectado = true;
			fcntl(this->fileDescriptor, F_SETFL, 0);
			return true;
		} else {
			fcntl(this->fileDescriptor, F_SETFL, 0);
			return false;
		}

	} else if(status == 0) {
		conectado = true;
		fcntl(this->fileDescriptor, F_SETFL, 0);
		return true;
	} else {
		fcntl(this->fileDescriptor, F_SETFL, 0);
		return false;
	}
}

bool Socket::listen(const unsigned int maxClientes)
{
	int listenReturn;

	if (!this->esValido())
	{
		return false;
	}

	listenReturn = ::listen(this->fileDescriptor, maxClientes);

	return (listenReturn != -1);
}

bool Socket::bind(const unsigned int port)
{
	if (!this->esValido())
	{
		return false;
	}

	this->address.sin_family = AF_INET;
	this->address.sin_addr.s_addr = INADDR_ANY;
	this->address.sin_port = htons(port);

	int bindReturn;

	bindReturn = ::bind(this->fileDescriptor,
			(struct sockaddr *) &this->address, sizeof(this->address));

	return (bindReturn != -1);
}

Socket* Socket::accept()
{
	int newFileDescriptor = ::accept(this->fileDescriptor, NULL, NULL);

	if (newFileDescriptor > 0)
	{
		return new Socket(newFileDescriptor);
	}
	else
	{
		return NULL;
	}
}

bool Socket::esValido() const
{
	return (fileDescriptor != -1);
}

bool Socket::send(const char *stream, unsigned int size) const
{
	unsigned int bytesRead = 0;
	int result;

	if (!this->conectado)
	{

		return false;
	}

	while (bytesRead < size)
	{
		result = ::send(this->fileDescriptor, stream + bytesRead, size
				- bytesRead, MSG_NOSIGNAL);
		if (result < 1)
		{
			std::cout << "no se puede leer del socket" << errno;
			std::cout.flush();
			return false;
		}
		bytesRead += result;
	}

	return true;
}

bool Socket::recv(char *stream, unsigned int size) const
{
	unsigned int bytesRead = 0;
	int result;

	if (!this->conectado)
	{
		return false;
	}

	while (bytesRead < size)
	{
		result = ::recv(this->fileDescriptor, stream + bytesRead, size
				- bytesRead, 0);
		if (result < 1)
		{
			std::cout << "no se puede leer del socket " << errno << endl;
			cout.flush();
			return false;
		}
		bytesRead += result;
	}
	return true;
}

int Socket::close()
{
	::close(this->fileDescriptor);
	this->conectado = false;
	return 0;
}

bool Socket::shutdown()
{
	::shutdown(this->fileDescriptor, SHUT_RDWR);
	this->conectado = false;
	return true;
}

bool Socket::getConectado()
{
	return this->conectado;
}
